package ace.module.account.core.impl.service.authentication.handler.impl.wxminiapp;

import ace.cmp.core.exception.BusinessException;
import ace.cmp.json.api.JsonService;
import ace.module.account.api.enums.IamAccountPassportPassportTypeEnum;
import ace.module.account.api.model.auth.AuthenticationGrantType;
import ace.module.account.api.model.auth.dto.AuthenticationResult;
import ace.module.account.api.model.auth.input.GenericAuthenticationInputImpl;
import ace.module.account.api.model.auth.input.params.AuthenticationParamsWxMpLoginCode;
import ace.module.account.core.impl.dao.entity.IamAccountPassport;
import ace.module.account.core.impl.manager.IamAccountPassportManager;
import ace.module.account.core.impl.manager.cache.IamAuthenticationLoginCacheManager;
import ace.module.account.core.impl.service.authentication.handler.RegisterQuickManager;
import ace.module.account.core.impl.service.authentication.handler.impl.AbstractAuthenticationHandler;
import ace.module.account.core.impl.service.authentication.handler.model.input.AuthenticationHandlerContextInput;
import ace.module.weixin.api.WxsMiniAppApi;
import ace.module.weixin.api.model.dto.WxsJsCode2SessionResultDto;
import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

/**
 * @author caspar
 * @date 2023/3/16 9:49 微信小程序Login code认证
 * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">微信小程序Login code认证</a>
 */
@Slf4j
@Component
public class WxMiniAppLoginCodeAuthenticationHandler
    extends AbstractAuthenticationHandler<
    AuthenticationParamsWxMpLoginCode,
    GenericAuthenticationInputImpl<AuthenticationParamsWxMpLoginCode>> {

  private final WxsMiniAppApi wxsMiniAppApi;
  private final IamAccountPassportManager iamAccountPassportManager;
  private final IamAuthenticationLoginCacheManager iamAuthenticationLoginCacheManager;
  private final RegisterQuickManager registerQuickManager;

  protected WxMiniAppLoginCodeAuthenticationHandler(
      JsonService jsonService,
      WxsMiniAppApi wxsMiniAppApi,
      IamAccountPassportManager iamAccountPassportManager,
      IamAuthenticationLoginCacheManager iamAuthenticationLoginCacheManager,
      RegisterQuickManager registerQuickManager) {
    super(AuthenticationGrantType.WX_MINI_LOGIN_CODE.getCode(), jsonService);
    this.wxsMiniAppApi = wxsMiniAppApi;
    this.iamAccountPassportManager = iamAccountPassportManager;
    this.iamAuthenticationLoginCacheManager = iamAuthenticationLoginCacheManager;
    this.registerQuickManager = registerQuickManager;
  }

  @Override
  protected AuthenticationResult doAuthentication(
      AuthenticationHandlerContextInput<AuthenticationParamsWxMpLoginCode> context) {
    Long iamAppId = context.getAuthenticationContext().getIamApplication().getId();
    String wxMiniAppId = context.getAuthenticationContext().getBizParams().getWxMiniAppId();
    String code = context.getAuthenticationContext().getBizParams().getCode();
    String tag = context.getAuthenticationContext().getTag();
    boolean registerQuickFlag =
        Boolean.TRUE.equals(
            context.getAuthenticationContext().getBizParams().getRegisterQuickFlag());
    WxsJsCode2SessionResultDto wxsJsCode2SessionResultDTO =
        this.wxsMiniAppApi.getOpenIdByLoginCode(wxMiniAppId, code).check();
    String openId = wxsJsCode2SessionResultDTO.getOpenId();
    String unionId = wxsJsCode2SessionResultDTO.getUnionId();
    if (StringUtils.isBlank(openId)) {
      log.info("doAuthentication_openid_fail,{}", wxsJsCode2SessionResultDTO);
      throw new BusinessException("获取openid失败");
    }
    IamAccountPassport accountPassport =
        this.getPassportByOpenId(iamAppId, tag, wxMiniAppId, openId);
    if (accountPassport == null && StringUtils.isNotEmpty(unionId)) {
      accountPassport = this.getPassportByUnionId(iamAppId, tag, wxMiniAppId, unionId);
    }

    registerQuickManager.ifSupportRegisterQuickThenAndThrow(
        accountPassport != null,
        registerQuickFlag,
        (sessionKey) -> {
          iamAuthenticationLoginCacheManager.setWxsJsCode2SessionResultDto(
              sessionKey, wxsJsCode2SessionResultDTO);
        });

    if (accountPassport == null) {
      throw new BusinessException("认证失败");
    }

    Long accountId = accountPassport.getIamAccountId();
    return AuthenticationResult.builder().accountId(accountId).attributes(new HashMap<>()).build();
  }

  private IamAccountPassport getPassportByUnionId(
      Long iamAppId, String tag, String wxMiniAppId, String unionId) {
    return iamAccountPassportManager
        .getByIamAppIdAndTagAndPassportAndPassport2AndPassport3AndPassportType(
            iamAppId,
            tag,
            wxMiniAppId,
            unionId,
            StringUtils.EMPTY,
            IamAccountPassportPassportTypeEnum.WX_MINI_APP_UNIONID.getCode());
  }

  private IamAccountPassport getPassportByOpenId(
      Long iamAppId, String tag, String wxMiniAppId, String openId) {
    return iamAccountPassportManager
        .getByIamAppIdAndTagAndPassportAndPassport2AndPassport3AndPassportType(
            iamAppId,
            tag,
            wxMiniAppId,
            openId,
            StringUtils.EMPTY,
            IamAccountPassportPassportTypeEnum.WX_MINI_APP_OPENID.getCode());
  }

  @Override
  public String getGrantType() {
    return AuthenticationGrantType.WX_MINI_LOGIN_CODE.getCode();
  }
}
